﻿using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;

namespace CxExtension
{
	public class NumSplit
	{
		protected double[] stepList; //
		protected string[] postList; //
		protected int mFloatByte = 0; //小数点位数
		protected string formatStr = ""; //小数点位数

		public NumSplit(double step, string[] postList)
		{
			var length = postList.Length;
			stepList = new double[length];
			var sum = 1d;
			for (var i = 0; i < length; i++)
			{
				sum *= step;
				stepList[i] = sum;
			}
			this.postList = postList;
		}
		public NumSplit(string[] postList) : this(1000f, postList)
		{
		}

		public void SetFloatByte(int v)
		{
			if (mFloatByte == v)
			{
				return;
			}
			mFloatByte = v;

			if (IsInteger)
			{
				formatStr = "{0}{1}";
			}
			else
			{
				var fStr = "F" + v;
				formatStr = "{0:" + fStr + "}{1}";
			}
		}

		public string ToNumStr(long value)
		{
			return ToNumAroundStr(value, StepFunc, UnitFunc,ToStringFunc, postList.Length);
		}

		private string ToStringFunc(long arg1, double arg2,string arg3)
		{
			var argNum = IsInteger?arg1:arg2;
			return string.Format(formatStr,argNum,arg3);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="self"></param>
		/// <param name="stepFunc"></param>
		/// <param name="unitFunc"></param>
		/// <param name="toStringFunc"></param>
		/// <param name="count"></param>
		/// <returns></returns>
		public string ToNumAroundStr(long self, Func<int, long> stepFunc, Func<int, string> unitFunc,Func<long,double,string,string> toStringFunc, int count = 10)
		{
			var lv = 0;
			var num = self;
			var reNum = self;
			var reNumF = (double)self;
			for (var i = 0; i < count; i++)
			{
				var step = stepFunc(i);
				var remain = num / step;
				if (remain > 0)
				{
					reNum = remain;
					reNumF = (double)num / step;
				}
				else
				{
					lv = i;
					break;
				}
			}
			var unitStr = unitFunc(lv);
			var str = toStringFunc(reNum, reNumF, unitStr);
			return str;
		}
		protected virtual long StepFunc(int arg1)
		{
			return (long)(stepList.Get(arg1,false));
		}

		protected virtual string UnitFunc(int arg1)
		{
			return postList[arg1];
		}

		public bool IsInteger => false/*mFloatByte <= 0*/;
	}
	public static class StringNumExt
	{
		private static readonly string[] mPostfix3 = { "无", "万", "亿" };                      //
		private static readonly string[] mPostfix2Bmp = { "0", "w", "y" };                      //
		private static readonly string[] postByteList = new []{ "B", "KB", "MB","GB","TB" };                      //
		private static readonly int byteStep = 1024;
		private static NumSplit mNumSplit;                      //
		private static NumSplit mByteSplit;                      //
		private static NumSplit mByteSplit2;                      //

		static StringNumExt()
		{
			mNumSplit= new NumSplit(mPostfix3);
			mByteSplit = new NumSplit(byteStep,postByteList);
			mByteSplit.SetFloatByte(0);
			mByteSplit2 = new NumSplit(byteStep,postByteList);
			mByteSplit2.SetFloatByte(2);
		}
		public static string ToNumStr(this long self)
		{
			return mNumSplit.ToNumStr(self);
		}

		public static string ToNumBmpStr(this long self)
		{
			return self.ToNumStr(mPostfix2Bmp);
		}
		/// <summary>
		/// to 1 "byte", "kb", "mb","gb","tb"
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static string ToByteViewStr(this long self)
		{
			return mByteSplit.ToNumStr(self);
		}
		/// <summary>
		/// to 1.23 "byte", "kb", "mb","gb","tb"
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static string ToByteViewStr2(this long self)
		{
			return mByteSplit2.ToNumStr(self);
		}

		public static string[] Split(this string self, params string[] argns)
		{
			return self.Split(argns, StringSplitOptions.None);
		}

		public static string ToNumAroundStr(this string self, [NotNull] Func<int, int> steps, [NotNull] Func<int, string> unitFunc, int count = 10)
		{
			if (steps == null) throw new ArgumentNullException("steps");
			if (unitFunc == null) throw new ArgumentNullException("unitFunc");

			var len = self.Len();
			if (len == 0)
			{
				return self;
			}
			var lv = 0;
			var byt = 0;
			for (int i = 0; i < count; i++)
			{
				var it = steps(i);
				if (len >= it)
				{
					lv = i;
					byt = it;
				}
				else
				{
					break;
				}
			}

			var numStr = self.Substring(0, len - byt + 1);
			var unitStr = unitFunc(lv);
			return numStr + unitStr;
		}

		public static string ToNumAroundStr(this long self, IList<int> stepList, IList<string> unitList)
		{
			var str = self.ToNumAroundStr(i => stepList[i], i1 => unitList[i1], stepList.Count);
			return str;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="self"></param>
		/// <param name="stepFunc"></param>
		/// <param name="unitFunc"></param>
		/// <param name="count"></param>
		/// <returns></returns>
		public static string ToNumAroundStr(this long self, Func<int, long> stepFunc, Func<int, string> unitFunc, int count = 10)
		{
			var lv = 0;
			var num = self;
			var renum = self;
			for (var i = 0; i < count; i++)
			{
				var step = stepFunc(i);
				var remain = num / step;
				if (remain > 0)
				{
					lv = i;
					renum = remain;
				}
				else
				{
					break;
				}
			}

			var unitStr = unitFunc(lv);
			return renum + unitStr;
		}

		public static string ToNumStr(this long self, string[] posfixs)
		{
			var wn = 10000;
			var yn = 100000000;
			var lastfix = "";
			var fenmu = 1;
			if (self == 0)
			{
				return posfixs.Get(0);
			}
			if (self >= wn)
			{
				lastfix = posfixs.Get(1);
				fenmu = wn;
			}
			else if (self >= yn)
			{
				lastfix = posfixs.Get(2);
				fenmu = yn;
			}

			return self / fenmu + lastfix;
		}
		public static string ToNumStr(this int self)
		{
			return ToNumStr((long)self);
		}
		public static string ToNumStr(this uint self)
		{
			return ToNumStr((long)self);
		}

	}
}